import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Transol implements Runnable{
    private ServerSocket userServer;//监听用户服务
    private ServerSocket clientServer;//监听客户端服务
    int clientPort = 8085;//客户端连接端口
    int userPort = 8082;//用户连接端口
    // 设置超时时间 90s
    private static int TIMEOUT = 90;
    // 客户端列表 用于删除失效连接和超时连接
    private static HashMap<Socket, Date> clientList = new HashMap<>();

    //任务队列
    List<Socket> list = new LinkedList<>();

    public static void main(String[] args) {
        new Thread(new Transol()).start();
    }

    @Override
    public void run() {
        new Thread(new client()).start();//服务端监控启动
        try {
            this.userServer = new ServerSocket(this.userPort);//监听8082用户的连接
            System.out.println("服务器开启成功");
            System.out.println("监听端口 : " + this.userPort);
        } catch (IOException e) {
            System.out.println("服务器开启失败");
            System.out.println(e.getMessage());
            System.out.println("退出运行");
            return;
        }
        // 自动清除失效连接和超时连接
        new Thread(new Terminal()).start();
        new Thread(new AutoDestroy()).start();

        while(true)
        {
            Socket userSocket = null;//用户请求
            try {
                userSocket = userServer.accept();
                // 接收到请求就把socket扔进map,value为刷新时间
                clientList.put(userSocket, new Date());
                String address = userSocket.getRemoteSocketAddress().toString();
                System.out.println("新连接 ： " + address);
                list.add(userSocket);
            } catch (IOException e) {
                System.out.println("连接异常");
                System.out.println(e.getMessage());
                close(userSocket);
            }

        }
    }
    //与客户端的交互类
    private class client implements Runnable
    {

        @Override
        public void run() {
            System.out.println("服务器端启动！");
            try {
                clientServer = new ServerSocket(clientPort);//监听8085客户端的连接
                while(true)
                {
                    Socket clientSocket = clientServer.accept();//一直存在
                    if(list.size() > 0)
                    {
                        System.out.println("list大于0，启动转发");
                        Socket temp = list.get(0);//取出第一个请求
                        list.remove(0);
                        new Thread(new swap(temp.getInputStream(),clientSocket.getOutputStream())).start();
                        new Thread(new swap(clientSocket.getInputStream(),temp.getOutputStream())).start();
                    }else{
                        clientSocket.close();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void close(Socket socket) {
        try {
            if (socket != null) {
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //读取输入流的函数
    private class swap implements Runnable //in->out
    {
        InputStream in = null;
        OutputStream out = null;
        swap(InputStream in,OutputStream out){
            this.in = in;
            this.out = out;
        }
        @Override
        public void run() {
            int length = 0;
            byte[] buffer = new byte[1024];
            try {
                while ((length = in.read(buffer)) > -1) {
                    out.write(buffer, 0, length);
                }
            } catch (IOException e) {
                System.out.println("出错，连接关闭");
            } finally {
                System.out.println("一次传输完成");
            }
        }
    }

    // 用于清除失效连接和超时连接
    private class AutoDestroy implements Runnable {

        @Override
        public void run() {

            ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);

            Runnable cleanTask = () -> {
                List<Socket> list = new LinkedList<>();
                System.out.println("开始扫描失效与超时连接,当前连接：" + clientList.size());
                Date start = new Date();
                for (Socket socket : clientList.keySet()) {
                    Date lastTime = clientList.get(socket);
                    long grapTime = System.currentTimeMillis() - lastTime.getTime();
                    if (socket.isClosed() || grapTime / 1000 >= TIMEOUT) {
                        list.add(socket);
                    }
                }
                System.out.println("找到" + list.size() + "个,查询用时 : " + (System.currentTimeMillis() - start.getTime()) + "毫秒");
                System.out.println("开始清除失效与超时连接");
                for (Socket socket : list) {
                    close(socket);
                    clientList.remove(socket);
                }
                System.out.println("清理后连接数 : " + clientList.size());
            };
            pool.scheduleWithFixedDelay(cleanTask, 0, 90, TimeUnit.SECONDS);
        }
    }

    private class Terminal implements Runnable {
        private String format = "yyyy-MM-dd HH:mm:ss";
        private SimpleDateFormat dateFormat = new SimpleDateFormat(format);

        @Override
        public void run() {
            while (!userServer.isClosed()) {
                System.out.println("请输入命令（status）：");
                Scanner scanner = new Scanner(System.in);
                String cmd = scanner.nextLine();
                handler(cmd);
            }
        }

        private void handler(String cmd) {
            switch (cmd) {
                case "status":
                    System.out.println("当前时间 : " + dateFormat.format(new Date()));
                    System.out.println("总连接数 : " + clientList.size());
                    for (Socket socket : clientList.keySet()) {
                        long time = System.currentTimeMillis() - clientList.get(socket).getTime();
                        System.out.println("<" + socket.getRemoteSocketAddress().toString() + "> " + time / 1000);
                    }
                    break;
            }
        }
    }
}
